// ColorRamp.cpp
//
#include "ColorRamp.h"
#include "Color.h"

using namespace OpenGL;

//Program ColorRamp::shader;

// --------------------------------------------------------------------------------------------
ColorRamp::ColorRamp()
{
	double inf = std::numeric_limits<double>::infinity();
	maxValue = -inf;
	minValue = inf;
	unit     = 0;
}

// --------------------------------------------------------------------------------------------
ColorRamp::~ColorRamp()
{
	destroy();
}

// --------------------------------------------------------------------------------------------
void ColorRamp::build(int resolution)
{
	if( entries.size() == 0 )
		return;

	unsigned char *pixels = new unsigned char[resolution * 4];
	
	maxValue = entries.rbegin()->first;
	minValue = entries.begin()->first;

	int k = 0;
	for( std::map<double, Color>::iterator nextCol = entries.begin();
		nextCol != entries.end(); 
		/**/)
	{
		std::map<double, Color>::iterator currCol = nextCol++;

		if( nextCol != entries.end() )
		{
			const double nValue   = (nextCol->first-minValue) / (maxValue-minValue); // [0..1]
			const int numPixels   = resolution * nValue;
			const int spanSize    = numPixels - (k/4);
			const double currStep = 1.0 / ((double) spanSize);

			double t = 0.0;
			for(int i=0; i<spanSize; ++i)
			{
				pixels[ k + 0 ] = (currCol->second.r * (1.0-t) + nextCol->second.r * t) * 255.f;
				pixels[ k + 1 ] = (currCol->second.g * (1.0-t) + nextCol->second.g * t) * 255.f;
				pixels[ k + 2 ] = (currCol->second.b * (1.0-t) + nextCol->second.b * t) * 255.f;
				pixels[ k + 3 ] = (currCol->second.a * (1.0-t) + nextCol->second.a * t) * 255.f;

				t+=currStep;

				k+=4;
			}
		}
	}

	ramp.create(0,4,GL_RGBA,GL_UNSIGNED_BYTE,0,resolution,pixels);
	ramp.setParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	ramp.setParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	ramp.setParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	ramp.setParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);

#if 0
	OpenGL::TextureUtility::dumpTGA("c:/users/ramp.tga",pixels,4,resolution,1);
#endif

	delete [] pixels;
}

// --------------------------------------------------------------------------------------------
//bool ColorRamp::initShader()
//{
//	// ----------------------------------
//	//              VERTEX               
//	// ----------------------------------
//	const std::string vs = STRINGIFY
//	(
//		varying float v_heightDelta;
//		void main()
//		{
//			vec4 p = vec4( gl_Vertex.xy, 0.0, 1.0 );
//			
//			gl_FrontColor     = gl_Color;
//			gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;
//			gl_Position       = gl_ModelViewProjectionMatrix*p;
//			v_heightDelta     = gl_Vertex.z;
//		}
//	);
//
//	// ----------------------------------
//	//             FRAGMENT              
//	// ----------------------------------
//	const std::string fs = STRINGIFY
//	(
//		varying float	  v_heightDelta;
//		uniform sampler2D u_tex;
//		uniform sampler1D u_ramp;
//		uniform vec2      u_heightMinMax;
//		uniform vec2      u_rampMinMax;
//			
//		void main()
//		{
//			float texel = texture2D(u_tex, (gl_TexCoord[0].xy) ).r;
//				
//			if(texel <= 0.0)	discard;
//				
//			float h    = 	texel;           				// h in 0..1
//			float minH = u_heightMinMax.x + v_heightDelta;  // adjust vertical datum
//			float maxH = u_heightMinMax.y + v_heightDelta;	// adjust vertical datum
//			h         *= ( maxH - minH );					// h in meters [0..(max-min)]
//			h         += minH;								// h in meters [min..max]
//
//			float t  = (h - u_rampMinMax.x) / (u_rampMinMax.y - u_rampMinMax.x) ;
//				
//			vec4 color   = texture1D( u_ramp, t);
//			//gl_FragColor = vec4( color.rgb, 0.2 ); // DEBUG PURPOSE
//			gl_FragColor = vec4( color.rgb, gl_Color.a );
//		}
//	);
//
//	shader.set(vs,fs);
//	std::string log =  shader.getInfoLog();
//	printf("ColorMap Shader LOG: %s\n",log.length() == 0 ? "Succeded" : log.c_str() );
//	
//	return shader.isValid();
//}


// --------------------------------------------------------------------------------------------
void ColorRamp::addColor(double position,const Color &color, bool autoRebuild)
{
	entries[position] = color;

	if(autoRebuild)
		build();

	if( position < minValue ) minValue = position;
	if( position > maxValue ) maxValue = position;
}

// --------------------------------------------------------------------------------------------
void ColorRamp::destroy()
{
	entries.clear();
	ramp.destroy();
}

// --------------------------------------------------------------------------------------------
//void ColorRamp::setCurrentMinMax(float minHeight, float maxHeight)
//{
//	shader.uniform("u_heightMinMax",minHeight,maxHeight);
//}

// --------------------------------------------------------------------------------------------
void ColorRamp::bind(unsigned int textureUnit)
{
	unit = textureUnit;
	glActiveTexture(GL_TEXTURE0 + unit);
	glEnable(GL_TEXTURE_1D);
	ramp.bind();
	glActiveTexture(GL_TEXTURE0);
	
	//shader.bind();

	////shader.uniform("u_sunDirection",1,0,0);
	//shader.uniform("u_rampMinMax",(float)minValue,(float)maxValue);
	//shader.uniform("u_tex",  0);
	//shader.uniform("u_ramp", 1);
}

// --------------------------------------------------------------------------------------------
void ColorRamp::unBind()
{
	glActiveTexture(GL_TEXTURE0 + unit);
	glDisable(GL_TEXTURE_1D);
	ramp.unBind();
	glActiveTexture(GL_TEXTURE0);
}